﻿namespace WLFramework.Mapping
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using WLFramework.Reflection;

    /// <summary>
    /// 类型映射关系元数据信息
    /// </summary>
    public class MapperInfo
    {
        /// <summary>
        /// 目标类型
        /// </summary>
        public Type To { get; private set; }
        
        /// <summary>
        /// 原始类型
        /// </summary>
        public Type From { get; private set; }

        /// <summary>
        /// 原始类型和目标类型是否均为基元类型
        /// </summary>
        public bool IsPrimitive { get; private set; }

        /// <summary>
        /// 属性/字段读取器，默认为NULL, 当前原始类型为属性时，由原始类型的拥有者构造读取器。
        /// </summary>
        public TargetGetter Getter { get; private set; }

        /// <summary>
        /// 属性/字段设置器，默认为NULL，当前目标类型为属性时， 由目标类型的拥有者构造设置器。
        /// </summary>
        public TargetSetter Setter { get; private set; }
        
        /// <summary>
        /// 只有当IsPrimitive为False时此属性生效， 表示原始类型和目标类型中的属性/字段映射关系集合
        /// </summary>
        public List<MapperInfo> MapperInfos { get; private set; }

        /// <summary>
        /// 目标类型的构造函数
        /// </summary>
        public TargetCreater Creater { get; private set; }

        /// <summary>
        /// 构造原始类型与目标类型之间的映射关系信息
        /// 条件：原始类型与目标类型均为基元类型， 或者 原始类型与目标类型均为“类”
        /// </summary>
        /// <param name="from">原始类型</param>
        /// <param name="to">目标类型</param>
        public MapperInfo(Type from, Type to)
        {
            this.IsPrimitive = from.IsPrimitiveType() && to.IsPrimitiveType();
            bool isClass = from.IsClass && to.IsClass;

            Shield.AssertIsTrue(this.IsPrimitive || isClass);

            this.From = from;
            this.To = to;

            if (!this.IsPrimitive)
            {
                this.MapperInfos = new List<MapperInfo>();
                var ctor = to.GetConstructor(Type.EmptyTypes);
                if (ctor != null)
                {
                    this.Creater = new TargetCreater(ctor.CreateConstructorDelegate(), to);
                }

                var fromMembers = InternalMemberInfo.GetMemberInfo(from).ToList();
                var toMembers = InternalMemberInfo.GetMemberInfo(to).ToList();
                for (int i = 0; i < fromMembers.Count; i++)
                {
                    var fromMember = fromMembers[i];

                    for (int j = 0; j < toMembers.Count; j++)
                    {
                        var toMember = toMembers[j];
                        bool isPrimitive;
                        if (CheckSupport(fromMember, toMember, out isPrimitive))
                        {
                            var mapper = new MapperInfo(
                                               fromMember.ValueType,
                                               toMember.ValueType);

                            mapper.Getter = fromMember.Getter();
                            mapper.Setter = toMember.Setter();
                            this.MapperInfos.Add(mapper);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 创建原始类型与目标类型之家的映射关系信息
        /// </summary>
        /// <typeparam name="TFrom">原始类型</typeparam>
        /// <typeparam name="TTo">目标类型</typeparam>
        /// <returns>类型映射关系元数据信息</returns>
        public static MapperInfo Create<TFrom, TTo>()
        {
            return new MapperInfo(typeof(TFrom), typeof(TTo));
        }

        /// <summary>
        /// 创建原始类型与目标类型之家的映射关系信息
        /// </summary>
        /// <param name="from">原始类型</param>
        /// <param name="to">目标类型</param>
        /// <returns>类型映射关系元数据信息</returns>
        public static MapperInfo Create(Type from, Type to)
        {           
            return new MapperInfo(from, to);
        }

        private static bool CheckSupport(
            InternalMemberInfo from, 
            InternalMemberInfo to,
            out bool isPrimitive)
        {
            isPrimitive = false;
            if (from.MemberName != to.MemberName)
                return false;

            if (from.ValueType.IsPrimitiveType() &&
                to.ValueType.IsPrimitiveType() &&
                from.ValueType == to.ValueType)
            {
                isPrimitive = true;
                return true;
            }

            if (from.ValueType.IsClass &&
                to.ValueType.IsClass)
            {
                return true;
            }

            return false;
        }
    }
}
